/*
 * QNXVoltage.c
 *
 *  Created on: Oct 28, 2011
 *      Author: Philip Rodriguez, Alfred Lee, Oliver Wing
 *
 *  QNX-side implementation which takes care of converting
 *  analog data to digital data, and then sending the data
 *  off to Freescale to display the voltage
 */

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/neutrino.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <sys/siginfo.h>
#include <unistd.h>       /* for sleep() */
#include <stdint.h>       /* for uintptr_t */
#include <hw/inout.h>     /* for in*() and out*() functions */
#include <sys/neutrino.h> /* for ThreadCtl() */
#include <sys/mman.h>     /* for mmap_device_io() */
#include "Converter.h"
#include "DigitalIO.h"
#include "Parallel.h"
#include "types.h"

// The timeout duration
#define TIMEOUT_DURATION_NANOSECONDS		(1000000000)

// The amount of time to wait before
// sending out the next set of data
#define DATA_SEND_DELAY_MICROSECONDS		(100000)

// FOREVER~
#define FOREVER								(1)

// Grant permission for this thread to
// access the hardware
void grantHardwarePermission(void);


// The interrupt id set up on the parallel port
INT16 interruptId;

struct sigevent freescaleIsAliveEvent;

int main(int argc, char *argv[]) {

	// return value from reading the A/D register
	INT16 adReturnValue;
	INT16 adReturnValueWithinRange;
	// converted value, in volts
	float convertedInputVoltage;

	// set up the timeout for the connection
	uint64_t timeoutSetTime;
	uint64_t timeoutRemTime;
	timeoutSetTime = TIMEOUT_DURATION_NANOSECONDS;
	timeoutRemTime = 0;

	// set up the parallel port life pulse.
	SIGEV_INTR_INIT(&freescaleIsAliveEvent);

	// grant permission to this thread to
	// access the hardware
	grantHardwarePermission();

	// set up the parallel port
	initParallelPort();

	// initialize the A/D register
	initADRegister();

	// initialize the digital I/O registers
	initDigitalIORegister();


	// Run "POSTs".
	if ( !POSTInputDAQ() )
	{
		ClearPOSTSuccess();
		OutputStatus();
		return EXIT_FAILURE;
	}
	if ( !POSTOutputDAQ() )
	{
		ClearPOSTSuccess();
		OutputStatus();
		return EXIT_FAILURE;
	}
	RaisePOSTSuccess();
	OutputStatus();



	// run forever
	while( FOREVER )
	{
		// set a timeout on the interrupt
		TimerTimeout(CLOCK_REALTIME, _NTO_TIMEOUT_INTR, NULL, &timeoutSetTime, &timeoutRemTime);
		// sleep until the next interrupt
		InterruptWait( 0, NULL );

		// wait a short moment for Freescale to
		// finish moving the servos
		usleep(DATA_SEND_DELAY_MICROSECONDS);

		// QNX is not yet ready to send data
		outputReadySignal(LOW);

		// if Freescale failed to send an interrupt,
		// then emit the error noise
		if (errno == ETIMEDOUT)
		{
			printf("Timeout occurred. Please check connection\r\n");

			// indicate that there is a connection error
			RaiseConnectionError();
			OutputStatus();
		}
		// if Freescale successfully sent an interrupt
		// before the timeout, perform the A/D conversion
		else if (errno == EOK)
		{
			printf("Connection successful. ");
			printf("Performing A/D conversion\r\n");

			// indicate there is no connection error,
			// and that the conversion is in progress
			ClearConnectionError();
			ConversionInProgress();
			OutputStatus();

			// perform the A/D conversion, get the digital data,
			// and indicate the voltage on this console
			performADConversion();
			adReturnValue = readADData();
			convertedInputVoltage = convertADData(adReturnValue);
			printf("Input Voltage: %0.3f V (%6d raw data) \r\n", convertedInputVoltage, adReturnValue);

			// Check that the input voltage is at reasonable and non-dangerous levels.
			if ( (adReturnValue > AD_ALLOWABLE_RANGE) || (adReturnValue < -AD_ALLOWABLE_RANGE) )
			{
				printf("Warning: Voltage out of range! \r\n");
				if ( adReturnValue > AD_ALLOWABLE_RANGE )
				{
					adReturnValueWithinRange = 0x7fff;	// Positive limit
				}
				else
				{
					adReturnValueWithinRange = 0x8000;	// Negative limit
				}
				// indicate a potentially dangerous voltage level
				RaiseVoltageWarning();
			}
			else
			{
				// indicate a safe voltage level
				adReturnValueWithinRange = adReturnValue << 1;
				ClearVoltageWarning();
			}

			// Ready to read.
			ConversionResultReady();
			OutputStatus();

			// QNX is ready to send data
			outputReadySignal(HIGH);
			// send the data
			sendData(adReturnValueWithinRange);
		}

		// Clear errno for next run.
		errno = 0;

		// re-enable this interrupt
		InterruptUnmask( PARALLEL_IRQ, interruptId );
	}

	// remove the attachment from this thread
	InterruptDetach( interruptId );

	// should never reach here
	return EXIT_SUCCESS;
}

// Grant permission for this thread to
// access the hardware
void grantHardwarePermission(void)
{
	int privityErr;

	// Give this thread root permissions to access the hardware
	privityErr = ThreadCtl( _NTO_TCTL_IO, NULL );
	if ( privityErr == -1 )
	{
		fprintf( stderr, "can't get root permissions\n" );
		exit(-1);
	}
}
